﻿#region

using System;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;

#endregion

namespace Core.DAO
{
    /// <summary>
    ///     This is base class of all Data Access class.
    ///     It contains general methods of Data Access class as following:
    ///     Add,Update,Delete,SaveChanges,GetByQuery
    ///     In BaseDAO,allways exitsed an ObjectContext to work with Data Enterprise to access database.
    /// </summary>
    /// <typepara name="TObjectContext">ObjectContext to access to database</typepara>
    /// <typepara name="TEntity">Entity object needs to be processed</typepara>
    /// <remarks>
    ///     created by: ThangLD
    ///     created date: 30 may 2009
    ///     modified by: N/A
    ///     modified date: N/A
    /// </remarks>
    public class BaseDAO<TObjectContext, TEntity> : IDAO<TEntity>
        where TObjectContext : ObjectContext, new()
        where TEntity : EntityObject
    {
        /// <summary>
        ///     Returns an ObjectContext object.
        /// </summary>
        public TObjectContext ObjectContext
        {
            get
            {
                var objectContext = DAOScope.GetCurrentObjectContext<TObjectContext>();
                var type = objectContext.GetType();
                objectContext.MetadataWorkspace.LoadFromAssembly(type.Assembly);
                return objectContext;
            }
        }

        #region IDAO<TEntity> Members

        /// <summary>
        ///     Adds a new entity object to the context.
        /// </summary>
        /// <para name="newEntity">A new object.</para>
        public int Add(TEntity newEntity)
        {
            ObjectContext.AddObject(newEntity.GetType().Name, newEntity);
            return ObjectContext.SaveChanges();
        }

        /// <summary>
        ///     Deletes an entity object.
        /// </summary>
        /// <para name="obsoleteEntity">An obsolete object.</para>
        public int Delete(TEntity obsoleteEntity)
        {
            object currentEntityInDb;
            ObjectContext.TryGetObjectByKey(obsoleteEntity.EntityKey, out currentEntityInDb);
            ObjectContext.DeleteObject(currentEntityInDb);
            return ObjectContext.SaveChanges();
        }

        /// <summary>
        ///     Update Entity Methode
        /// </summary>
        /// <para name="updatedEntity">entity to update</para>
        public int Update(TEntity updatedEntity)
        {
            try
            {
                object currentEntityInDb;
                ObjectContext.TryGetObjectByKey(updatedEntity.EntityKey, out currentEntityInDb);
                ObjectContext.ApplyPropertyChanges(((TEntity) currentEntityInDb).EntityKey.EntitySetName, updatedEntity);
                return ObjectContext.SaveChanges();
            }
            catch (Exception)
            {
                throw new ObjectNotFoundException();
            }
        }

        /// <summary>
        ///     Get List of Entity by Queryable
        /// </summary>
        /// <typepara name="TEntity"></typepara>
        /// <para name="query"></para>
        /// <returns></returns>
        public ReadOnlyCollection<TEntity> GetByQuery(IQueryable<TEntity> query)
        {
            return new ReadOnlyCollection<TEntity>(query.ToList());
        }


        /// <summary>
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public IQueryable<TEntity> Search(string query)
        {
            var objectQuery = new ObjectQuery<TEntity>(query, ObjectContext, MergeOption.NoTracking);
            return objectQuery;
        }

        #endregion

        public void SaveChanges()
        {
            ObjectContext.SaveChanges();
        }
    }
}